﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

namespace eBrain
{
    public class EyeTracker
    {

        //Eyelink related
        public static SREYELINKLib.EL_EYE eye;// = SREYELINKLib.EL_EYE.EL_EYE_NONE;
        public double st;
        public static SREYELINKLib.EyeLinkUtil elutil;// = new SREYELINKLib.EyeLinkUtil();
        public static SREYELINKLib.EyeLink el;// = new SREYELINKLib.EyeLink();

        public Logger myLog;

        private String myEDFFilename; 

        public EyeTracker(Logger _Log) 
        {
            myLog = _Log;

            try
            {
                //Impostazioni per l'Eyetracker
                eye = SREYELINKLib.EL_EYE.EL_EYE_NONE;
                elutil = new SREYELINKLib.EyeLinkUtil();
                el = new SREYELINKLib.EyeLink();
            }
            catch (Exception ex)
            {
                MessageBox.Show("EyeTracker error:" + ex.Message + " " + ex.Source.ToString());
            }

        
        }


        public void ClearScreen()
        {
                el.sendCommand("clear_screen 7");
        }


        public void SendCommand(String s)
        {
            el.sendCommand(s);
        }


        public void SendMessage(String s)
        {
            el.sendMessage(s);
        }

        public bool TestEyelinkConnection()
        {
            bool isAvailable = false;
            
            try
            {
                myLog.Log("Open Eyelink Connection");
                el.open("100.1.1.1", 0);
                StartRecording();
                Thread.Sleep(300);
                StopRecording(false, "test.edf");
                
                Thread.Sleep(200);
                isAvailable = el.isConnected();
                el.close();
                
                return isAvailable;
            }
            catch (Exception ex)
            {
                myLog.Log("TestEyelinkConnection error:" + ex.Message + " " + ex.Source.ToString());
                return false;
            }
        }


        public bool ET_OpenEyelinkConnection()
        {
            try
            {
                myLog.Log("Open Eyelink Connection");
                el.open("100.1.1.1", 0);
                return el.isConnected();
            }
            catch (Exception ex)
            {
                myLog.Log("OpenLinkConnection error:" + ex.Message + " " + ex.Source.ToString());
                return false;
            }
        }


        public bool IsConnected()
        {
            return el.isConnected();
        }

        public void Close()
        {
            el.close();
        }


        public void SuperInitialize(Form myWindowForm, String EDFFilename, int ScreenWidth, int ScreenHeight, bool IsSaccade, int ActualTrialNumber)
        {

            myLog.Log("trying el.open");
            el.open("100.1.1.1", 0);

            myLog.Log("trying openDataFile");
            el.openDataFile("twt01.edf");

            myLog.Log("trying el.sendCommand(''link_sample_data  = LEFT,RIGHT,GAZE'');");
            el.sendCommand("link_sample_data  = LEFT,RIGHT,GAZE");

            myLog.Log("trying el.sendCommand - screen_pixel_coord= 0,0," + (ScreenWidth - 1).ToString() + "," + (ScreenHeight - 1).ToString());
            el.sendCommand("screen_pixel_coords = 0 0 " + (ScreenWidth - 1).ToString() + "," + (ScreenHeight - 1).ToString());

            el.sendMessage("DISPLAY_COORDS 0 0 " + (ScreenWidth - 1).ToString() + "," + (ScreenHeight - 1).ToString());

            el.sendCommand("record_status_message '" + ActualTrialNumber + "'");

            //Mnado il TrialID prima di iniziare
            el.sendMessage("TRIALID " + ActualTrialNumber);

            System.Threading.Thread.Sleep(100);

            //myLog.Log("trying el.open");
            //el.open("100.1.1.1", 0);

            //myLog.Log("trying openDataFile");
            //el.openDataFile("TWT-001.edf");

            //myLog.Log("trying el.sendCommand(''link_sample_data  = LEFT,RIGHT,GAZE'');");
            //el.sendCommand("link_sample_data  = LEFT,RIGHT,GAZE");

            //myLog.Log("trying el.sendCommand - screen_pixel_coord= 0,0," + this.Width + "," + this.Height);
            //el.sendCommand("screen_pixel_coords=0,0," + this.Width + "," + this.Height);

            myLog.Log("trying el.sendMessage (twt01)");
            el.sendMessage("twt01");

            //Entering the Calibration phase

            System.Threading.Thread.Sleep(100);

            myLog.Log("trying SREYELINKLib.ELGDICal cal = elutil.getGDICal();");
            SREYELINKLib.ELGDICal cal = elutil.getGDICal();

            myLog.Log("trying cal.setCalibrationWindow(this.Handle.ToInt32());");
            cal.setCalibrationWindow(myWindowForm.Handle.ToInt32());

            cal.setCalibrationTargetSize(4, 8);

            cal.setCalibrationColors(0x00ffffff, 0x000000);

            //myLog.Log("trying cal.clearCalibrationDisplay();");
            cal.clearCalibrationDisplay();

            myLog.Log("cal.enableKeyCollection(true);");
            cal.enableKeyCollection(true);

            Console.WriteLine("SuperInitialize - stiamo per entrare nel doTrackerSetup()");

            myLog.Log("el.doTrackerSetup();");
            el.doTrackerSetup();

            Console.WriteLine("SuperInitialize - usciti dal doTrackerSetup()");

            myLog.Log("cal.enableKeyCollection(false);");
            cal.enableKeyCollection(false);

            myLog.Log("cal.enableKeyCollection(true);");
            cal.enableKeyCollection(true);

            myLog.Log("el.doDriftCorrect(" + ((short)(ScreenWidth / 2)).ToString() + "," + ((short)(ScreenHeight / 2)).ToString() + ", true, true)");
            el.doDriftCorrect((short)(ScreenWidth / 2), (short)(ScreenHeight / 2), true, true);

            myLog.Log("cal.enableKeyCollection(false);");
            cal.enableKeyCollection(false);

            //Cancello l'area
            el.sendCommand("clear_screen 7");


        }


        public bool ConnectEyelink(String EDFFilename, int ScreenWidth, int ScreenHeight, bool IsSaccade, int ActualTrialNumber)
        {
            bool Connected = false;

            //if (!bEyelinkConnected)
            //{
            try
            {
                if (!el.isConnected())
                {
                    myLog.Log("trying el.open");
                    el.open("100.1.1.1", 0);
                }

                myLog.Log("trying openDataFile :" + EDFFilename);
                el.openDataFile(EDFFilename);

                myEDFFilename = EDFFilename;

                myLog.Log("trying el.sendCommand(''link_sample_data  = LEFT,RIGHT,GAZE'');");
                el.sendCommand("link_sample_data  = LEFT,RIGHT,GAZE");

                 myLog.Log("trying el.sendCommand - screen_pixel_coord= 0,0," + (ScreenWidth - 1).ToString() + "," + (ScreenHeight - 1).ToString());
                el.sendCommand("screen_pixel_coords = 0 0 " + (ScreenWidth - 1).ToString() + " " + (ScreenHeight - 1).ToString());

                el.sendMessage("DISPLAY_COORDS 0 0 " + (ScreenWidth - 1).ToString() + " " + (ScreenHeight - 1).ToString());

                //--------------- TODO: verificare che cosi' funzioni
                el.sendCommand("record_status_message '" + "Trial ID=" + ActualTrialNumber.ToString() + "'");

                //[per DataViewer] Mando la sequenza dei nomi delle variabili prima di iniziare
                if (IsSaccade)
                    el.sendMessage("TRIAL_VAR_LABELS SubjectID Type Delay");
                else
                    el.sendMessage("TRIAL_VAR_LABELS SubjectID");

                //[per DataViewer] Mando il TrialID prima di iniziare
                el.sendMessage("TRIALID " + ActualTrialNumber.ToString());

                el.sendMessage("!V TRIAL_VAR " + ActualTrialNumber.ToString());

                //---------------- FINE TODO: verificare che cosi' funzioni

                //Ok, let's consider the Eyelink connected
                Connected  = true;
            }
            catch (Exception ex)
            {
                myLog.Log("ConnectToEyelink error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                MessageBox.Show("ConnectToEyelink error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);

                Connected  = false;
            }
            return Connected;
            //}
        }

        public void CalibrateEyelink(Int32 FormHandle, Color bkgrnd, Color frgrnd, int ScreenWidth, int ScreenHeight)
        {
           
            try
            {
                
                Thread.Sleep(100);
                                
                myLog.Log("trying SREYELINKLib.ELGDICal cal = elutil.getGDICal();");
                SREYELINKLib.ELGDICal cal = elutil.getGDICal();

                myLog.Log("trying cal.setCalibrationWindow(this.Handle.ToInt32());");
                cal.setCalibrationWindow(FormHandle);//myExpW.Handle.ToInt32());

                cal.setCalibrationTargetSize(4, 8);

                uint bg = ColorToIntRGB(bkgrnd);
                uint fg = ColorToIntRGB(frgrnd);

                cal.setCalibrationColors(fg, bg);

                //myLog.Log("trying cal.clearCalibrationDisplay();");
                cal.clearCalibrationDisplay();

                myLog.Log("cal.enableKeyCollection(true);");
                cal.enableKeyCollection(true);

                myLog.Log("el.doTrackerSetup();");
                el.doTrackerSetup();

                myLog.Log("cal.enableKeyCollection(false);");
                cal.enableKeyCollection(false);

                myLog.Log("cal.enableKeyCollection(true);");
                cal.enableKeyCollection(true);

                myLog.Log("el.doDriftCorrect(" + ((short)(ScreenWidth / 2)).ToString() + "," + ((short)(ScreenHeight / 2)).ToString() + ", true, true)");
                el.doDriftCorrect((short)(ScreenWidth / 2), (short)(ScreenHeight / 2), true, true);

                myLog.Log("cal.enableKeyCollection(false);");
                cal.enableKeyCollection(false);

                //Cancello l'area
                el.sendCommand("clear_screen 7");

                //Imposto l'occhio da acquisire
                eye = (SREYELINKLib.EL_EYE)el.eyeAvailable();

                //this.setGazeCursor(true);
                //Ending the calibration phase
                System.Threading.Thread.Sleep(100);

                //TODO: questa è una parte che deve rimanere nella classe Experiment
                //myExpW.BackColor = bkgrnd;
                //myExpW.Invalidate();

            }
            catch (Exception e)
            {
                myLog.Log("CalibrateEyelink Error: " + e.Message.ToString() + "\r\n" + e.StackTrace);
                MessageBox.Show("CalibrateEyelink Error: " + e.Message.ToString());
            }
        }

        public void UpdateGaze(ref int act_x, ref int act_y)
        {
            float x = 0, y = 0;
            SREYELINKLib.Sample s;
            s = el.getNewestSample();
            if (s != null)
            {
                if (eye == SREYELINKLib.EL_EYE.EL_LEFT)
                {
                    x = s.get_gx(SREYELINKLib.EL_EYE.EL_LEFT);
                    act_x = (int)x;

                    y = s.get_gy(SREYELINKLib.EL_EYE.EL_LEFT);
                    act_y = (int)y;
                }
                else if (eye == SREYELINKLib.EL_EYE.EL_RIGHT || eye == SREYELINKLib.EL_EYE.EL_BINOCULAR)
                {
                    x = s.get_gx(SREYELINKLib.EL_EYE.EL_RIGHT);
                    act_x = (int)x;
                    y = s.get_gy(SREYELINKLib.EL_EYE.EL_RIGHT);
                    act_y = (int)y;
                }
            }
        }


        //public void LabelTrial(bool IsSaccade, int ActualTrialNumber)
        //{
        //    el.sendCommand("record_status_message '" + "Trial ID=" + ActualTrialNumber.ToString() + "'");

        //    //[per DataViewer] Mando la sequenza dei nomi delle variabili prima di iniziare
        //    if (IsSaccade)
        //        el.sendMessage("TRIAL_VAR_LABELS SubjectID Type Delay");
        //    else
        //        el.sendMessage("TRIAL_VAR_LABELS SubjectID");

        //    //[per DataViewer] Mando il TrialID prima di iniziare
        //    el.sendMessage("TRIALID " + ActualTrialNumber.ToString());

        //    el.sendMessage("!V TRIAL_VAR " + ActualTrialNumber.ToString());
        //}

        public void TestEyelinkCalibration( int ScreenWidth,int  ScreenHeight, int iColorCounter)
        {
            try
            {
                myLog.Log("trying el.open");
                el.open("100.1.1.1", 0);

                myLog.Log("trying el.sendCommand - screen_pixel_coord= 0,0," + ScreenWidth + "," + ScreenHeight);
                el.sendCommand("screen_pixel_coords = 0 0 " + (ScreenWidth - 1).ToString() + " " + (ScreenHeight - 1).ToString());

                el.sendMessage("DISPLAY_COORDS 0 0 " + (ScreenWidth - 1).ToString() + " " + (ScreenHeight - 1).ToString());

                el.sendCommand("record_status_message 'Test Calibration'");

                //Cancello l'area con un colore di sfondo crescente (per debug)
                el.sendCommand("clear_screen " + iColorCounter.ToString());
                if (++iColorCounter > 15)
                    iColorCounter = 0;

                int size = 100;
                int border = 30;
                int delta_w = (ScreenWidth - 3 * size - 2 * border) / 3;
                int delta_h = (ScreenHeight - 3 * size - 2 * border) / 3;

                for (int k = 0; k < 3; k++)
                {
                    for (int l = 0; l < 3; l++)
                    {
                        int x0, y0, xf, yf, c;
                        x0 = k * delta_w + border;
                        y0 = k * delta_h + border;
                        xf = x0 + size;
                        yf = y0 + size;
                        c = k * 3 * l;
                        string s = "draw_filled_box " + x0.ToString() + " " + y0.ToString() + " " + xf.ToString() + " " + yf.ToString() + " " + c.ToString();
                        el.sendCommand(s);

                    }
                }

                //Facciamo una prova per vedere i colori selezionati
                //el.sendCommand("draw_filled_box 100 100 200 200 0");
                //el.sendCommand("draw_filled_box 200 100 300 200 1");
                //el.sendCommand("draw_filled_box 300 100 400 200 2");
                //el.sendCommand("draw_filled_box 400 100 500 200 3");
                //el.sendCommand("draw_filled_box 500 100 600 200 4");
                //el.sendCommand("draw_filled_box 600 100 700 200 5");
                //el.sendCommand("draw_filled_box 700 100 800 200 6");
                //el.sendCommand("draw_filled_box 800 100 900 200 7");
                //el.sendCommand("draw_filled_box 900 100 1000 200 8");
                //el.sendCommand("draw_filled_box 100 200 200 300 9");
                //el.sendCommand("draw_filled_box 200 200 300 300 10");
                //el.sendCommand("draw_filled_box 300 200 400 300 11");
                //el.sendCommand("draw_filled_box 400 200 500 300 12");
                //el.sendCommand("draw_filled_box 500 200 600 300 13");
                //el.sendCommand("draw_filled_box 600 200 700 300 14");
                //el.sendCommand("draw_filled_box 700 200 800 300 15");

                //0 = black
                //1 = blue
                //2 = verde 
                //3 = turchese
                //4 = rosso
                //5 = viola
                //6 = ocra
                //7 = grigio
                //8 = grigio scuro
                //9 = carta da zucchero
                //10 = verde pallido
                //11 = azzutto chiaro
                //12 = rosa
                //13 = lilla
                //14 = giallo pallido
                //15 = bianco
                //el.sendMessage("TRIAL_VAR_LABELS " + "SUBJECT_ID "

                //Ok, let's consider the Eyelink connected
                //bEyelinkConnected = true;
            }
            catch (Exception ex)
            {
                myLog.Log("TestEyelinkCalibration error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                MessageBox.Show("TestEyelinkCalibration error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
            }
        }


        public void StartRecording()
        {
            if (el.isConnected())
            {
                myLog.Log("el.startRecording(true, true, true, true);");

                el.startRecording(true, true, true, true);
                //The first parameter is whether to record samples
                //the second is whether to record events (saccades, fixations, blinks)
                //the third is whether to make samples available over the link
                //and the fourth is whether to make events available over the link.

                myLog.Log("st = elutil.currentTime();");
                st = elutil.currentTime();

                //StartCountdownTimer();
            }
            myLog.Log("Start Recording Trial Data");
        }

        public void StopRecording(bool bSaveFile, String Filename)
        {
            myLog.Log("Stop Recording Trial Data");

            //Se non sto usando il mouse in emulazione, allora chiudo il collegamento anche con l'Eyelink
            if (el.isConnected())
            {
                try
                {
                    el.stopRecording();
                    if (bSaveFile)
                        el.receiveDataFile(myEDFFilename , Filename);//EDFFilename, sDataPath + "\\" + FileNameResults + ".edf");
                }
                catch (Exception ex)
                {
                    myLog.Log("StopRecording Error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                    MessageBox.Show("StopREcording Error: " + ex.Message.ToString() + "\r\n" + ex.StackTrace);
                }
            }
        }

        public void CloseEyetrackerConnection()
        {
            try
            {
                if (el.isConnected())
                    el.close();

                //if (bEyelinkConnected)
                //{
                //}
            }
            catch (Exception ex)
            {
                myLog.Log("CloseConnection Error: " + ex.Message + " " + ex.InnerException.ToString());
            }

            //bEyelinkConnected = false;
        }


        public uint ColorToIntRGB(Color c)
        {
            uint val = 0;
            val += ((uint)c.R) << 16;
            val += ((uint)c.G) << 8;
            val += (uint)c.B;

            return val;
        }

    }
}
